#pragma once

#include<memory>
#include<set>
#include <functional>
#include "thread.h"
#include "util.h"

namespace yuechuan {
    class TimerManager;

    /**
     * @brief 定时器类
     * @inherit enable_shared_from_this 作用是使当前对象只能作为智能指针存在
     */
    class Timer : public std::enable_shared_from_this<Timer> {
        friend class TimerManager;

    public:
        typedef std::shared_ptr<Timer> ptr;

        /**
         * @brief 取消定时器
         * @return bool 当定时器绑定了回调函数，将当前定时器从其绑定的Manager中删除，成功返回true
         *              没有绑定回调函数，则返回false
         */
        bool cancel();

        /**
         * @brief 刷新定时器
         * @details 将该定时器从m_timers中取出
         *          然后将其m_next设置为当前时间加上其周期
         *          最后再将其放到m_timers中
         * @return bool 当在其绑定的Manager中的m_timers中没有找到该定时器时，返回false，成功刷新返回true
         */
        bool refresh();

        /**
         * @brief 重置该定时器
         * @details 将定时器的周期设置为传入的新周期的值
         * @param ms 新的周期
         * @param from_now 是否从当前时间设置m_next 下一次执行的时间
         * @return bool 传入的周期为和之前的周期相同且不更改m_next 直接返回true，成功也返回true，没有找到该定时器返回false
         */
        bool reset(uint64_t ms, bool from_now);

    private:

        /**
         * @brief 含参构造，只能通过定时器管理类来进行创建
         * @param ms 定时器的周期毫秒数
         * @param cb 定时器绑定的回调函数
         * @param recurring 是否循环执行
         * @param manager 定时器绑定的定时器管理对象
         */
        Timer(uint64_t ms, std::function<void()> cb, bool recurring, TimerManager *manager);

        /**
         * @brief 含参构造
         * @param next 下一次执行的时间
         */
        Timer(uint64_t next);

        /**
         * @brief 定时器的比较器
         * @details 先判断两个定时器是否为空
         *          然后根据其下次触发时间进行比较，下次触发时间较小的，放在前面
         *          当下次触发时间一致时，根据其地址进行排序，地址小的在前面
         */
        struct Comparator {
            bool operator()(const Timer::ptr &lhs, const Timer::ptr &rhs) const;
        };

    private:
        /// 是否是循环计时器
        bool m_recurring = false;
        /// 执行周期,epoll只支持毫秒级别，所以在定时器中只需要实现毫秒级别
        uint64_t m_ms = 0;
        /// 精确的执行时间  当前时间+定时器时间
        uint64_t m_next = 0;
        /// 计时器执行的回调函数
        std::function<void()> m_cb;
        /// 创建该计时器的对应的管理类
        TimerManager *m_manager{};
    };

    class TimerManager {
        friend class Timer;

    public:
        /// 要实现读写分离的锁
        typedef RWMutex RWMutexType;

        /**
         * @brief 无参构造函数
         */
        TimerManager();

        /**
         * @brief 这里将析构函数设置为虚函数是为了在IOManager中继承该类
         */
        virtual ~TimerManager();

        /**
         * @brief 新增一个定时器
         * @details 要判断插入的定时器的时间是否是最小的时间
         *          因为我们对set提供了排序算法，是按照其实际时间来进行排序的
         *              需要的就是判断其插入的返回值的first是否和set的begin()相等即可
         *              相等就要通知IOManager立刻停止epoll_wait将其中的等待时间更新为当前新添加的定时器的时间
         * @param ms 定时器的周期
         * @param cb 定时器的回调函数
         * @param recurring 定时器是否是循环
         * @return Timer::ptr 返回刚刚新增的定时器智能指针
         */
        Timer::ptr addTimer(uint64_t ms, std::function<void()> cb, bool recurring = false);

        /**
         * @brief 新增一个条件定时器
         * @details 传递的weak_ptr当其对应的引用消失了，就代表条件不满足，不需要执行
         * @param ms 定时器的执行周期
         * @param cb 定时器的回调函数
         * @param weak_cond 条件定时器的条件变量
         * @param recurring 是否循环
         * @return Timer::ptr 新添加的定时器智能指针
         */
        Timer::ptr addConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond,
                                     bool recurring = false);

        /**
         * @brief 获取下一个定时器的执行时间
         * @return uint64_t 当前定时器的下一个定时器的m_next 减去当前时间 的值， 当m_next小于当前时间，直接返回0
         */
        uint64_t getNextTimer();

        /**
         * @brief 查询超时的定时器
         * @details 在m_timers中找到第一个小于等于当前时间的定时器
         *          将其后面的都删除，放到一个数组中，调用其回调函数
         *          对于要循环执行的定时器，更新m_next，再次添加到定时器队列中
         *          对于只需要执行一次的定时器执行完成后将其回调函数置为nullptr防止再次调用
         * @param cbs
         */
        void listExpiredCb(std::vector<std::function<void()> > &cbs);

        /**
         * @brief 是否含有定时器
         * @return bool
         */
        bool hasTimer();

    protected:

        /**
         * @brief 创建了新的定时器，且插入的位置是最前面，需要调用的方法
         * @details 因为想定时器列表中插入数据是按照下一次执行的时间由小到大的顺序插入的
         *          则插入一个新的定时器在最前面
         *          在epoll_wait中假如等待的时间为10s
         *          新插入的定时器为3秒，
         *          则在10s后执行就已经超时了，所以在插入一个定时器在最前面就要尝试唤醒epoll_wait,更新新的定时器
         */
        virtual void onTimerInsertForFront() = 0;

        /**
         * @brief 传入一个定时器和一个写锁来添加一个定时器
         * @details 一般在公有方法中调用该方法
         * @param time 定时器指针
         */
        void addTimer(Timer::ptr time, RWMutexType::WriteLock &);

    private:
        /**
         * @brief 检测系统时间是否被调整提前
         * @details 当发现当前服务器时间小于上一次执行时间， 并且小于上一次时间多于1小时
         * @param now_ms 当前系统的时间
         * @return bool 系统时间是否被调整提前
         */
        bool detectClockRollover(uint64_t now_ms);

    private:
        RWMutexType m_mutex;
        /// 定时器集合 要做到有序，需要传递比较算法
        std::set<Timer::ptr, Timer::Comparator> m_timers;
        /// 是否要触发在定时器队列第一个位置新增定时器
        bool m_tickled = false;
        /// 上一次执行定时器的时间
        uint64_t m_previousTime = 0;
    };
} // namespace yuechuan
